package com.yunshipei.core.manager;

import android.annotation.SuppressLint;
import android.content.Context;
import android.text.TextUtils;

import com.yunshipei.core.R;
import com.yunshipei.core.common.Callback;
import com.yunshipei.core.common.DeviceInfo;
import com.yunshipei.core.common.XCloudException;
import com.yunshipei.core.net.EnterClient;
import com.yunshipei.core.net.ServiceApi;
import com.yunshipei.core.net.convert.JsonConverterFactory;
import com.yunshipei.core.utils.FileUtils;
import com.yunshipei.core.utils.YspLogUtils;

import org.chromiun.net.GURLUtils;
import org.json.JSONObject;
import org.reactivestreams.Publisher;
import org.xwalk.core.XWalkPreferences;
import org.xwalk.core.XWalkView;

import java.io.File;
import java.util.List;

import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.exceptions.CompositeException;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.schedulers.Schedulers;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;

public class XCloudSDKManager {

    private static final boolean LIB_KERNEL = true;
    private static final String LIB_KERNEL_NAME = "xwalkcore";

    static final String SP_CONFIG_FILE_NAME = "xCloud_config";

    protected XCloudSDKManager() {
    }

    private static final class XCloudSDKManagerHolder {
        private static final XCloudSDKManager instance = new XCloudSDKManager();
    }

    public static XCloudSDKManager getInstance() {
        return XCloudSDKManagerHolder.instance;
    }

    private String mCompanyName = "";
    private String mManagerAddress = "";
    private DeviceInfo mDeviceInfo;
    private String mKernelPath;

    private boolean authSuccess = false;
    private boolean isConfigSuccess = false;

    public void init(Context context) {
        try {
            this.mKernelPath = context.getDir("xwalk_lib", Context.MODE_PRIVATE).getPath();
            this.mDeviceInfo = new DeviceInfo(context);
            String license = FileUtils.readAssertResource(context, "config.metadata");
            if (!TextUtils.isEmpty(license)) {
                JSONObject jsonObject = new JSONObject(license);
                this.mCompanyName = jsonObject.optString("companyName", "");
                this.mManagerAddress = jsonObject.optString("managerServer", "");
                String managerProxy = jsonObject.optString("managerProxy", "");
                String proxyIP = "";
                int proxyPort = 0;
                if (!TextUtils.isEmpty(managerProxy)) {
                    String proxy = managerProxy;
                    if (proxy.startsWith("http://")) {
                        proxy = proxy.replace("http://", "");
                    }
                    if (proxy.startsWith("https://")) {
                        proxy = proxy.replace("https://", "");
                    }
                    if (proxy.endsWith("/")) {
                        proxy = proxy.replace("/", "");
                    }
                    int index = proxy.indexOf(":");
                    if (index != -1) {
                        String[] arr = proxy.split(":");
                        proxyIP = arr[0];
                        proxyPort = Integer.parseInt(arr[1]);
                    }
                }
                EnterClient.getInstances().initManagerClient(mManagerAddress + "/v2/newLogin", proxyIP, proxyPort);
            }
            privateInit(context);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void configServer(Context context, String managerServer, String companyName, String proxyIP, int proxyPort) {
        this.mManagerAddress = managerServer;
        this.mCompanyName = companyName;
        this.mKernelPath = context.getDir("xwalk_lib", Context.MODE_PRIVATE).getPath();
        this.mDeviceInfo = new DeviceInfo(context);
        EnterClient.getInstances().initManagerClient(managerServer + "/v2/newLogin", proxyIP, proxyPort);
        privateInit(context);
    }

    private void privateInit(Context context) {
        RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {
                throwable.printStackTrace();
            }
        });
        if (LIB_KERNEL) {
            System.loadLibrary(LIB_KERNEL_NAME);
            initKernelConfig();
        }
        LogManager.getInstance().init(context);
        isConfigSuccess = !TextUtils.isEmpty(mCompanyName) && !TextUtils.isEmpty(mManagerAddress);
        //解决部分机型先创建android自带WebView对象然后加载java开发的系统导致的乱码问题
        XWalkView xWalkView = new XWalkView(context);
        xWalkView.onDestroy();
    }

    private void initKernelConfig() {
        XWalkPreferences.setValue(XWalkPreferences.ANIMATABLE_XWALK_VIEW, true);
        XWalkPreferences.setValue(XWalkPreferences.REMOTE_DEBUGGING, true);
    }

    public void authRequest(final Context context, final Callback callback) {
        if (!isConfigSuccess) {
            callback.error(context.getString(R.string.check_sdk_config));
        } else {
            if (!authSuccess) {
                OkHttpClient client = EnterClient.getInstances().getManagerClient();
                final ServiceApi serviceApi = new Retrofit.Builder().client(client).baseUrl(mManagerAddress).addConverterFactory(JsonConverterFactory.create()).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build().create(ServiceApi.class);
                Flowable<JSONObject> preWork = serviceApi.authCompany(mCompanyName);
                if (!LIB_KERNEL) {
                    preWork = Flowable.zip(preWork, new KernelHandler(context, mManagerAddress, mKernelPath, serviceApi).prepareKernel(), new BiFunction<JSONObject, Boolean, JSONObject>() {
                        @SuppressLint("UnsafeDynamicallyLoadedCode")
                        @Override
                        public JSONObject apply(JSONObject jsonObject, Boolean aBoolean) throws Exception {
                            System.load(mKernelPath + File.separator + "lib" + LIB_KERNEL_NAME + ".so");
                            initKernelConfig();
                            return jsonObject;
                        }
                    });
                }
                preWork.flatMap(new Function<JSONObject, Publisher<JSONObject>>() {
                    @Override
                    public Publisher<JSONObject> apply(JSONObject jsonObject) throws Exception {
                        YspLogUtils.d("cpmanyInfo:" + jsonObject.toString());
                        int status = jsonObject.optInt("status", 0);
                        switch (status) {
                            case 1:
                                JSONObject dataObject = jsonObject.optJSONObject("data");
                                if (dataObject != null) {
                                    String companyID = dataObject.optString("companyId", "");
                                    String devicesID = mDeviceInfo.getDevicesID();
                                    long timeStamp = System.currentTimeMillis();
                                    JSONObject sdkSumSrc = new JSONObject();
                                    sdkSumSrc.put("sdk", companyID);
                                    sdkSumSrc.put("deviceId", devicesID);
                                    sdkSumSrc.put("timestamp", timeStamp);
                                    String sdkSum = GURLUtils.getSDKSum(sdkSumSrc.toString());
                                    return serviceApi.authDevice(companyID, timeStamp, mDeviceInfo.allInfo(), sdkSum);
                                } else {
                                    return Flowable.error(new XCloudException(context.getString(R.string.no_error_msg_interface)));
                                }
                            case 0:
                                String message = jsonObject.optString("message", context.getString(R.string.no_error_msg_interface));
                                return Flowable.error(new XCloudException(message));
                        }
                        return Flowable.error(new XCloudException(context.getString(R.string.no_error_msg_interface)));
                    }
                }).flatMap(new Function<JSONObject, Publisher<JSONObject>>() {
                    @Override
                    public Publisher<JSONObject> apply(JSONObject jsonObject) throws Exception {
                        YspLogUtils.d("newLogin:" + jsonObject.toString());
                        int status = jsonObject.optInt("status");
                        if (status != 1) {
                            return Flowable.error(new XCloudException(jsonObject.optString("message", context.getString(R.string.no_error_msg_interface))));
                        }
                        return prepareXCloudData(context, mManagerAddress, jsonObject);
                    }
                }).subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<JSONObject>() {
                    @Override
                    public void accept(JSONObject jsonObject) throws Exception {
                        authSuccess = true;
                        callback.success();
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        callback.error(generateError(throwable));
                    }
                });
            } else {
                callback.success();
            }
        }
    }

    public Flowable<JSONObject> prepareXCloudData(Context context, String managerServer, JSONObject jsonObject) throws Exception {
        return new DataConfiguration(context, managerServer, jsonObject).config();
    }

    public Flowable<JSONObject> prepareXCloudData(Context context, String managerServer, JSONObject jsonObject, boolean isCachedLoginData) throws Exception {
        return new DataConfiguration(context, managerServer, jsonObject, isCachedLoginData).config();
    }

    public String generateError(Throwable throwable) {
        String errorMsg = "";
        if (throwable instanceof XCloudException) {
            errorMsg = throwable.getMessage();
        } else if (throwable instanceof CompositeException) {
            CompositeException compositeException = (CompositeException) throwable;
            List<Throwable> exceptions = compositeException.getExceptions();
            for (Throwable throwable1 : exceptions) {
                String msg = generateError(throwable1);
                if (TextUtils.isEmpty(errorMsg)) {
                    errorMsg += msg;
                } else {
                    errorMsg += ("\n" + msg);
                }
            }
        } else {
            errorMsg = throwable.getMessage();
            if (TextUtils.isEmpty(errorMsg)) {
                errorMsg = throwable.toString();
                if (TextUtils.isEmpty(errorMsg)) {
                    errorMsg = "未知错误...";
                }
            }
        }
        return errorMsg;
    }
}
